﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Drawing;
using System.Globalization;
using System.ComponentModel;
using System.Data;

namespace ProyectoPractico
{
    class Art
    {
        //CONSTANTES****************************************************************************************
        private const double ALFA = 0.1;
        private double BETA = 0.99;

        //ATRIBUTOS*****************************************************************************************
        private PesosCapa pesosIda;// de entrada a salida
        //private PesosCapa pesosVuelta;//de salida a entrada
        private bool[] asociada;
        //private double[] TEntrada;
        private ArrayList TSalida;
        private ArrayList entrada;
        /*private double alfa;
        private double beta;*/
        private double epsilon = -0.001;//****************************sino funca cambiarlo
        private double paramV;
        private double paramBase;
        private int numNeuronasE;


        
        //METODOS*****************************************************************************************

        //-------------------------------------------------------------------------------------------------
        /*
         * Constructor de la clase
         */
        public Art(int neuronasEntrada,double paramBase/*,double[] entradas*/)
        {
            numNeuronasE = neuronasEntrada;
            //TEntrada = new double[neuronasEntrada*2];
            TSalida = new ArrayList();
            this.paramV = paramBase;
            this.paramBase = paramBase;
            pesosIda = new PesosCapa(neuronasEntrada * 2);
            //pesosVuelta = new PesosCapa(neuronasEntrada * 2);
            entrada = new ArrayList();
            asociada = new bool[neuronasEntrada * 2];
            //complementarEntrada(entradas);
        }

        void setParamV(double paramV) {
            this.paramV = paramV;
        }
        /*
         * Método que complementa la entrada
         */
        private void complementarEntrada(ArrayList e)
        {
            
            for (int i = 0; i < e.Count; i++)
            {
                entrada.Add((double)e[i]);
            }
            for (int a = 0; a < e.Count; a++)
            {
                entrada.Add(1-(double)e[a]);
            }
        }

        /*
         * Método que determina si una neurona cumple el criterio
         */
        private bool pasaParamV(int numN)
        {
            bool pasa = false;
            double num = 0.0;
            double denom = 0.0;
            for (int i = 0; i < entrada.Count; i++)
            {
                num += Math.Min(pesosIda.getPeso(i,numN), (double)entrada[i]);//aqui cambie el getPeso
                denom += (double)entrada[i];
            }
            double formula = num / denom;
            if (formula >= paramV)
            {
                pasa = true;
            }
            return pasa;
        }

        /*
         * Método que reinicia el parámetro de vigilancia
         */
        public void resetParamV()
        {
            paramV = paramBase;
        }


        /*
         * Método que retorna la posición de la neurona
         * con mayor resonancia
         */
        private int escogerMasResonante()
        {
            int masR = 0;
            for (int i = 0; i < TSalida.Count; i++)
            {
                if ((double)(TSalida[i]) > (double)(TSalida[masR]))
                {
                    masR = i;
                }
            }
            return masR;
        }

        /*
         * Método que incrementa el parámetro de vigilancia
         * de la red ART
         */
        public void aumentarParamV(int numN)
        { //la neurona q recibe es de la capa de salida
            /*double num = 0.0;
            double denom = numNeuronasE;
            for (int i = 0; i < TSalida.Count; i++)
            {
                num += Math.Min((double)entrada[i], pesosIda.getPeso(i,numN));
            }
            paramV = (num / denom) + epsilon;*/

            double num = 0.0;
            double denom = 0.0;
            for (int i = 0; i < entrada.Count; i++)
            {
                num += Math.Min(pesosIda.getPeso(i, numN), (double)entrada[i]);//aqui cambie el getPeso
                denom += (double)entrada[i];
            }
            double formula = num / denom;
            paramV = formula + epsilon;
        }
        /*
         * Método que retorna un peso de una capa dada
         * 0 pesosIda, 1 pesosVuelta
         */
        public double getPeso(int numN, int pesoReq)
        {
            double peso = -1;

                peso = pesosIda.getPeso(numN, pesoReq);

            return peso;
        }
        /*
         * Método que cambia el valor de un
         * peso específico
         */
        public void setPeso(int numN, int pesoC, double nuevoP)
        {

                pesosIda.setPeso(numN, pesoC, nuevoP);

        }

        /*
         * Método que retorna una entrada específica del vector I
         */
        public double getEntrada(int pos)
        {
            return (double)entrada[pos];
        }
        /*
         * Método que calcula la T de una neurona
         */
        private double calcularT(int neuronaN)
        {
            double respuesta = 0;
            double respuestaTemp1 = 0;
            double respuestaTemp2 = 0;
            //calculamos el |I^W|
            for (int x = 0; x < entrada.Count; x++)
            {
                respuestaTemp1 += Math.Min((double)entrada[x], pesosIda.getPeso(x, neuronaN));
            }

            //calculamos el |'a' + W|
            for (int x = 0; x < entrada.Count; x++)
            {
                respuestaTemp2 += pesosIda.getPeso(x, neuronaN) + ALFA;
            }

            //calculamos |I^W| entre |'a' + W|
            respuesta = respuestaTemp1 / respuestaTemp2;

            return respuesta;
        }

        public void guardarPesosIda(string nombreArchivo)
        {
            pesosIda.guardarPesos(nombreArchivo);
        }


        public void cargarPesosIda(string nombreArchivo)
        {
            pesosIda.cargarPesos(nombreArchivo);
        }


        /*
         * 
         */
        public void asociarNeurona(int numN)
        {
            asociada[numN] = true;
        }

        /*
         * Método que retorna la cantidad de neuronas de entrada
         * de la ART
         */
        public int getNumEntradas()
        {
            return entrada.Count;
        }


        public void calcularTs(ArrayList entrada){
            complementarEntrada(entrada);
            resetParamV();
            int salidas = pesosIda.getPesosocupados();
            for (int a = 0; a < salidas; a++)
            {
                double numero = calcularT(a);
                TSalida.Insert(a, numero);
            }            
        }

        public void descalificarNeurona(int neurona) {
            TSalida[neurona] = -1.0;
        }
        public int buscarPosibleApto(/*int ultimaJRevisada*/) { 
            bool encontrado = false;
            int nResonante = -1;
            int salidas = pesosIda.getPesosocupados();
            while (!encontrado && salidas > 0){
                salidas--;
                nResonante = escogerMasResonante();
                if ((double)TSalida[nResonante]==-1.0){
                    salidas=0;
                    encontrado=false;
                }else if(!pasaParamV(nResonante)){
                    descalificarNeurona(nResonante);// TSalida[nResonante] = -1.0;
                    //aumentarParamV(nResonante);<---------------------------------------------------
                }else{
                    encontrado = true;
                }
                
            }
            return encontrado? nResonante : -1;       
        }

        public int crearNuevaSalida() {
            pesosIda.incrementarPesosOcupados();
            int nueva = pesosIda.getPesosocupados() - 1;
            ajustarPesos(nueva);
            return nueva;
        }

        public int clasificarEntrada(ArrayList entrada)
        {
            complementarEntrada(entrada);
            resetParamV();
            int salidas = pesosIda.getPesosocupados();
            for (int a = 0; a < salidas; a++)
            {
                double numero = calcularT(a);
                TSalida.Insert(a,numero);
            }
            bool asignado = false;
            int nResonante = -1;
            while(!asignado && salidas > 0){
                salidas--;
                nResonante = escogerMasResonante();
                if (!pasaParamV(nResonante))
                {
                    TSalida[nResonante]=-1.0;
                    //aumentarParamV(nResonante);<---------------------------------------------------
                }
                else {
                    asignado = true;                    
                    ajustarPesos(nResonante);              
                                      
                }
            }
            if (!asignado) {
                pesosIda.incrementarPesosOcupados();
                nResonante = pesosIda.getPesosocupados()-1;
                ajustarPesos(nResonante);
            }
            return nResonante;
        }

        public void ajustarPesos(int nResonante){
            for (int x = 0; x < numNeuronasE; x++) {
                double pesoW = pesosIda.getPeso(x,nResonante);
                double newPeso = BETA * (Math.Min((double)entrada[x],pesoW)) + (1-BETA)* pesoW;
                pesosIda.setPeso(x, nResonante, newPeso);            
            }
        }
        public void setBeta(int num) {
            BETA = num;
        }
        public void vaciarVectores()
        {
            TSalida.Clear();
            entrada.Clear();
        }
    }
}
